package com.aiapphub.SystemCode.service;

import com.aiapphub.SystemCode.dto.DifyFileInfo;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Dify客户端服务 - 使用OkHttp直接调用Dify API
 */
@Slf4j
@Service
public class DifyClientService {
    
    @Value("${dify.api.key:}")
    private String difyApiKey;
    
    @Value("${dify.app.id:}")
    private String difyAppId;


    
    private OkHttpClient httpClient;

    @Value("${dify.api.url:}")
    private String DIFY_API_BASE_URL ;
    
    @PostConstruct
    public void init() {
        // 优化OkHttpClient配置，增加超时时间以支持长时间的流式响应
        this.httpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)          // 连接超时时间
                .readTimeout(3600, TimeUnit.SECONDS)          // 读取超时时间，增加到1小时，支持长时间流式响应
                .writeTimeout(60, TimeUnit.SECONDS)           // 写入超时时间
                .pingInterval(30, TimeUnit.SECONDS)           // 心跳间隔，保持长连接活跃
                .connectionPool(new ConnectionPool(           // 连接池配置
                        20,                                  // 最大空闲连接数
                        30, TimeUnit.MINUTES))                // 连接最大空闲时间
                .retryOnConnectionFailure(true)               // 连接失败时自动重试
                .build();
        
        log.info("DIFY_API_BASE_URL==============================:" + DIFY_API_BASE_URL);
        if (difyApiKey != null && !difyApiKey.trim().isEmpty() &&
             DIFY_API_BASE_URL != null && !DIFY_API_BASE_URL.trim().isEmpty()) {
            log.info("Dify客户端初始化成功");
        } else {
            log.warn("Dify API密钥或连接地址未配置，客户端未初始化");
        }
    }
    
    /**
     * 发送聊天消息（流式模式）- 使用JSON字符串参数
     * @param requestUrl  目标请求URL（例如: https://api.dify.ai/v1/chat-messages）
     * @param difyApiKey  Dify API Key
     * @param requestJson 前端传入的JSON字符串，里面的属性将被动态合并到请求体
     */
    public void sendChatMessageStream(String requestUrl, String difyApiKey, String requestJson, StreamResponseHandler handler) {
        try {
            // 解析JSON请求参数
            JSONObject requestParams = JSON.parseObject(requestJson);
            
            // 验证关键参数
            if (difyApiKey == null || difyApiKey.trim().isEmpty()) {
                throw new RuntimeException("Dify API密钥未提供");
            }
            if (requestUrl == null || requestUrl.trim().isEmpty()) {
                throw new RuntimeException("请求URL未提供");
            }
            
            // 构建请求体
            Map<String, Object> requestBody = new HashMap<>();
            // 1) 默认字段（若前端未提供则使用默认值）
            requestBody.put("response_mode", "streaming");
            // 默认 inputs
            Map<String, Object> defaultInputs = new HashMap<>();
            defaultInputs.put("user_info", "");
            requestBody.put("inputs", defaultInputs);

            // 2) 动态合并所有字段
            // 移除不应进入请求体的字段（如果前端错误地传了进来）
            requestParams.remove("requestUrl");
            requestParams.remove("difyApiKey");

            // 兼容性：若只提供了 userId 则映射为 user（Dify要求为 user）
            if (!requestParams.containsKey("user") && requestParams.containsKey("userId")) {
                requestParams.put("user", requestParams.getString("userId"));
            }

            // files 可能是字符串，也可能是数组对象；统一转为数组对象
            if (requestParams.containsKey("files")) {
                Object filesVal = requestParams.get("files");
                try {
                    if (filesVal instanceof String) {
                        List<DifyFileInfo> files = JSON.parseArray((String) filesVal, DifyFileInfo.class);
                        if (files != null && !files.isEmpty()) {
                            requestParams.put("files", files);
                        }
                    }
                } catch (Exception e) {
                    log.warn("解析files失败: {}", e.getMessage());
                }
            }

            // inputs 优先覆盖：若前端提供 inputs 则完全覆盖默认值
            if (requestParams.containsKey("inputs")) {
                Object inputsVal = requestParams.get("inputs");
                try {
                    if (inputsVal instanceof String) {
                        // 允许字符串形式的 JSON
                        Map parsedInputs = JSON.parseObject((String) inputsVal, Map.class);
                        if (parsedInputs != null) {
                            requestBody.put("inputs", parsedInputs);
                        }
                    } else if (inputsVal instanceof Map) {
                        requestBody.put("inputs", inputsVal);
                    } else {
                        // 其他类型直接放入，交由服务端容错
                        requestBody.put("inputs", inputsVal);
                    }
                } catch (Exception e) {
                    log.warn("解析inputs失败: {}", e.getMessage());
                }
            }

            // 将 requestParams 全量合并到 requestBody（覆盖默认值，inputs 已提前处理）
            for (Map.Entry<String, Object> entry : requestParams.entrySet()) {
                requestBody.put(entry.getKey(), entry.getValue());
            }
            
            String jsonBody = JSON.toJSONString(requestBody);
            log.info("最终请求体JSON: ========================================================================{}", jsonBody);
            RequestBody body = RequestBody.create(jsonBody, MediaType.get("application/json; charset=utf-8"));
            
            Request request = new Request.Builder()
                    .url(requestUrl)
                    .post(body)
                    .addHeader("Authorization", "Bearer " + difyApiKey)
                    .addHeader("Content-Type", "application/json")
                    .build();
            
            httpClient.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    log.error("流式请求失败", e);
                    handler.onError(e);
                }
                
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (response.isSuccessful()) {
                        try (ResponseBody responseBody = response.body()) {
                            if (responseBody != null) {
                                String line;
                                while ((line = responseBody.source().readUtf8Line()) != null) {
                                    //System.out.println("line=========================="+line);
                                    if (line.startsWith("data: ")) {
                                        String data = line.substring(6);
                                        if ("[DONE]".equals(data)) {
                                            handler.onComplete();
                                            break;
                                        }
                                        try {
                                            JSONObject jsonData = JSON.parseObject(data);
                                            if(jsonData.containsKey("event"))
                                            {
                                                String event = jsonData.getString("event");
                                                if("message_end".equals(event))
                                                {
                                                    log.info("收到 message_end 事件，完整数据: {}", jsonData.toString());
                                                    // 提取 message_end 中的完整数据（包括 files、metadata 等）
                                                    JSONObject finalData = new JSONObject();
                                                    
                                                    // 提取 conversation_id
                                                    if (jsonData.containsKey("conversation_id")) {
                                                        finalData.put("conversation_id", jsonData.getString("conversation_id"));
                                                    }
                                                    
                                                    // 提取 metadata（包含引用、建议问题等）
                                                    if (jsonData.containsKey("metadata")) {
                                                        finalData.put("metadata", jsonData.getJSONObject("metadata"));
                                                    }
                                                    
                                                    // 提取 files（Dify 返回的文件列表）
                                                    if (jsonData.containsKey("files")) {
                                                        log.info("检测到 files 字段: {}", jsonData.get("files"));
                                                        finalData.put("files", jsonData.get("files"));
                                                    }
                                                    
                                                    // 提取 task_id
                                                    if (jsonData.containsKey("task_id")) {
                                                        finalData.put("task_id", jsonData.getString("task_id"));
                                                    }
                                                    
                                                    handler.onComplete(finalData);
                                                    break;
                                                }
                                            }
                                            if (jsonData.containsKey("answer")) {
                                                handler.onMessage(jsonData.getString("answer"));
                                            }
                                            // 处理会话ID
                                            if (jsonData.containsKey("conversation_id")) {
                                                String difyConversationId = jsonData.getString("conversation_id");
                                                //log.info("收到Dify会话ID: {}", difyConversationId);
                                                handler.onConversationId(difyConversationId);
                                            }
                                        } catch (Exception e) {
                                            log.warn("解析流式数据失败: {}", data, e);
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        String errorBody = response.body() != null ? response.body().string() : "Unknown error";
                        log.error("流式API调用失败，状态码: {}, 错误信息: {}", response.code(), errorBody);
                        handler.onError(new RuntimeException("流式API调用失败: " + errorBody));
                    }
                }
            });
            
        } catch (Exception e) {
            log.error("调用Dify流式API异常", e);
            handler.onError(e);
        }
    }
    
    /**
     * 执行 Workflow（流式模式）- 使用JSON字符串参数
     * @param requestUrl  目标请求URL（例如: https://api.dify.ai/v1/workflows/run）
     * @param difyApiKey  Dify API Key
     * @param requestJson 前端传入的JSON字符串，里面的属性将被动态合并到请求体
     * @param handler Workflow 流式响应处理器
     */
    public void sendWorkflowStream(String requestUrl, String difyApiKey, String requestJson, WorkflowStreamResponseHandler handler) {
        try {
            // 解析JSON请求参数
            JSONObject requestParams = JSON.parseObject(requestJson);
            
            // 验证关键参数
            if (difyApiKey == null || difyApiKey.trim().isEmpty()) {
                throw new RuntimeException("Dify API密钥未提供");
            }
            if (requestUrl == null || requestUrl.trim().isEmpty()) {
                throw new RuntimeException("请求URL未提供");
            }
            
            // 构建请求体
            Map<String, Object> requestBody = new HashMap<>();
            // 默认字段
            requestBody.put("response_mode", "streaming");
            
            // 移除不应进入请求体的字段
            requestParams.remove("requestUrl");
            requestParams.remove("difyApiKey");
            requestParams.remove("appName");
            
            // 兼容性：若只提供了 userId 则映射为 user（Dify要求为 user）
            if (!requestParams.containsKey("user") && requestParams.containsKey("userId")) {
                requestParams.put("user", requestParams.getString("userId"));
            }
            
            // 处理 inputs（必填）
            if (!requestParams.containsKey("inputs")) {
                requestBody.put("inputs", new HashMap<>());
            } else {
                Object inputsVal = requestParams.get("inputs");
                try {
                    if (inputsVal instanceof String) {
                        Map parsedInputs = JSON.parseObject((String) inputsVal, Map.class);
                        if (parsedInputs != null) {
                            requestBody.put("inputs", parsedInputs);
                        }
                    } else if (inputsVal instanceof Map) {
                        requestBody.put("inputs", inputsVal);
                    }
                } catch (Exception e) {
                    log.warn("解析inputs失败: {}", e.getMessage());
                    requestBody.put("inputs", new HashMap<>());
                }
            }
            
            // 处理 files
            if (requestParams.containsKey("files")) {
                Object filesVal = requestParams.get("files");
                try {
                    if (filesVal instanceof String) {
                        List<DifyFileInfo> files = JSON.parseArray((String) filesVal, DifyFileInfo.class);
                        if (files != null && !files.isEmpty()) {
                            requestBody.put("files", files);
                        }
                    } else if (filesVal instanceof List) {
                        // 如果是 List，直接使用（可能是 List<Map> 或 List<DifyFileInfo>）
                        List<?> filesList = (List<?>) filesVal;
                        if (!filesList.isEmpty()) {
                            requestBody.put("files", filesVal);
                            log.info("添加 files 参数到请求体，数量: {}", filesList.size());
                        }
                    } else {
                        log.warn("files 参数类型不正确，期望 List 或 String，实际: {}", 
                            filesVal != null ? filesVal.getClass().getName() : "null");
                    }
                } catch (Exception e) {
                    log.error("解析files失败: {}", e.getMessage(), e);
                }
            }
            
            // 将 requestParams 全量合并到 requestBody
            for (Map.Entry<String, Object> entry : requestParams.entrySet()) {
                if (!entry.getKey().equals("inputs") && !entry.getKey().equals("files")) {
                    requestBody.put(entry.getKey(), entry.getValue());
                }
            }
            
            String jsonBody = JSON.toJSONString(requestBody);
            log.info("Workflow 最终请求体JSON: {}", jsonBody);
            RequestBody body = RequestBody.create(jsonBody, MediaType.get("application/json; charset=utf-8"));
            
            Request request = new Request.Builder()
                    .url(requestUrl)
                    .post(body)
                    .addHeader("Authorization", "Bearer " + difyApiKey)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("Accept", "text/event-stream")
                    .build();
            
            httpClient.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    log.error("Workflow 流式请求失败", e);
                    handler.onError(e);
                }
                
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    // 注意：不要在这里读取 response.body()，因为 ResponseBody 只能读取一次
                    // System.out.println("Workflow response:===================================================== "+response.body().string());
                    if (response.isSuccessful()) {
                        try (ResponseBody responseBody = response.body()) {
                            if (responseBody != null) {
                                String line;
                                while ((line = responseBody.source().readUtf8Line()) != null) {
                                    System.out.println("Workflow response:===================================================== "+line);
                                    if (line.startsWith("data: ")) {
                                        String data = line.substring(6).trim();
                                        if ("[DONE]".equals(data)) {
                                            handler.onComplete();
                                            break;
                                        }
                                        if (data.isEmpty()) {
                                            continue;
                                        }
                                        try {
                                            JSONObject jsonData = JSON.parseObject(data);
                                            String event = jsonData.getString("event");
                                            String taskId = jsonData.getString("task_id");
                                            String workflowRunId = jsonData.getString("workflow_run_id");
                                            
                                            if (event != null) {
                                                switch (event) {
                                                    case "workflow_started":
                                                        JSONObject workflowData = jsonData.getJSONObject("data");
                                                        if (workflowData != null) {
                                                            handler.onWorkflowStarted(
                                                                workflowRunId,
                                                                taskId,
                                                                workflowData.getString("workflow_id")
                                                            );
                                                        }
                                                        break;
                                                        
                                                    case "node_started":
                                                        JSONObject nodeData = jsonData.getJSONObject("data");
                                                        if (nodeData != null) {
                                                            handler.onNodeStarted(
                                                                nodeData.getString("node_id"),
                                                                nodeData.getString("node_type"),
                                                                nodeData.getString("title"),
                                                                nodeData.getIntValue("index")
                                                            );
                                                        }
                                                        break;
                                                        
                                                    case "text_chunk":
                                                        JSONObject textData = jsonData.getJSONObject("data");
                                                        if (textData != null) {
                                                            String text = textData.getString("text");
                                                            Object fromVar = textData.get("from_variable_selector");
                                                            String[] fromVariableSelector = null;
                                                            if (fromVar instanceof List) {
                                                                List<?> list = (List<?>) fromVar;
                                                                fromVariableSelector = list.stream()
                                                                    .map(String::valueOf)
                                                                    .toArray(String[]::new);
                                                            }
                                                            handler.onTextChunk(text, fromVariableSelector);
                                                        }
                                                        break;
                                                        
                                                    case "node_finished":
                                                        JSONObject nodeFinishData = jsonData.getJSONObject("data");
                                                        if (nodeFinishData != null) {
                                                            handler.onNodeFinished(
                                                                nodeFinishData.getString("node_id"),
                                                                nodeFinishData.getString("status"),
                                                                nodeFinishData.get("outputs")
                                                            );
                                                        }
                                                        break;
                                                        
                                                    case "workflow_finished":
                                                        JSONObject workflowFinishData = jsonData.getJSONObject("data");
                                                        if (workflowFinishData != null) {
                                                            handler.onWorkflowFinished(
                                                                workflowRunId,
                                                                workflowFinishData.getString("status"),
                                                                workflowFinishData.get("outputs")
                                                            );
                                                            handler.onComplete();
                                                        }
                                                        break;
                                                        
                                                    case "tts_message":
                                                        handler.onTtsMessage(
                                                            jsonData.getString("message_id"),
                                                            jsonData.getString("audio")
                                                        );
                                                        break;
                                                        
                                                    case "tts_message_end":
                                                        handler.onTtsMessageEnd(jsonData.getString("message_id"));
                                                        break;
                                                        
                                                    case "ping":
                                                        // ping 事件，保持连接存活，无需处理
                                                        break;
                                                        
                                                    default:
                                                        log.debug("未处理的事件类型: {}", event);
                                                }
                                            }
                                        } catch (Exception e) {
                                            log.warn("解析 Workflow 流式数据失败: {}", data, e);
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        String errorBody = response.body() != null ? response.body().string() : "Unknown error";
                        log.error("Workflow 流式API调用失败，状态码: {}, 错误信息: {}", response.code(), errorBody);
                        handler.onError(new RuntimeException("Workflow API调用失败: " + errorBody));
                    }
                }
            });
            
        } catch (Exception e) {
            log.error("调用 Workflow 流式API异常", e);
            handler.onError(e);
        }
    }
    
    /**
     * 执行 Workflow（阻塞模式）
     * @param requestUrl  目标请求URL
     * @param difyApiKey  Dify API Key
     * @param requestJson 请求JSON字符串
     * @return 响应结果
     */
    public Map<String, Object> sendWorkflowBlocking(String requestUrl, String difyApiKey, String requestJson) {
        try {
            // 解析JSON请求参数
            JSONObject requestParams = JSON.parseObject(requestJson);
            
            // 验证关键参数
            if (difyApiKey == null || difyApiKey.trim().isEmpty()) {
                throw new RuntimeException("Dify API密钥未提供");
            }
            if (requestUrl == null || requestUrl.trim().isEmpty()) {
                throw new RuntimeException("请求URL未提供");
            }
            
            // 构建请求体
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("response_mode", "blocking");
            
            // 移除不应进入请求体的字段
            requestParams.remove("requestUrl");
            requestParams.remove("difyApiKey");
            requestParams.remove("appName");
            
            // 兼容性：若只提供了 userId 则映射为 user
            if (!requestParams.containsKey("user") && requestParams.containsKey("userId")) {
                requestParams.put("user", requestParams.getString("userId"));
            }
            
            // 处理 inputs
            if (!requestParams.containsKey("inputs")) {
                requestBody.put("inputs", new HashMap<>());
            } else {
                Object inputsVal = requestParams.get("inputs");
                if (inputsVal instanceof Map) {
                    requestBody.put("inputs", inputsVal);
                } else if (inputsVal instanceof String) {
                    Map parsedInputs = JSON.parseObject((String) inputsVal, Map.class);
                    if (parsedInputs != null) {
                        requestBody.put("inputs", parsedInputs);
                    }
                }
            }
            
            // 处理 files
            if (requestParams.containsKey("files")) {
                requestBody.put("files", requestParams.get("files"));
            }
            
            // 合并其他字段
            for (Map.Entry<String, Object> entry : requestParams.entrySet()) {
                if (!entry.getKey().equals("inputs") && !entry.getKey().equals("files")) {
                    requestBody.put(entry.getKey(), entry.getValue());
                }
            }
            
            String jsonBody = JSON.toJSONString(requestBody);
            log.info("Workflow 阻塞模式请求体JSON: {}", jsonBody);
            RequestBody body = RequestBody.create(jsonBody, MediaType.get("application/json; charset=utf-8"));
            
            Request request = new Request.Builder()
                    .url(requestUrl)
                    .post(body)
                    .addHeader("Authorization", "Bearer " + difyApiKey)
                    .addHeader("Content-Type", "application/json")
                    .build();
            
            try (Response response = httpClient.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    return JSON.parseObject(responseBody, Map.class);
                } else {
                    String errorBody = response.body() != null ? response.body().string() : "Unknown error";
                    log.error("Workflow 阻塞模式API调用失败，状态码: {}, 错误信息: {}", response.code(), errorBody);
                    throw new RuntimeException("Workflow API调用失败: " + errorBody);
                }
            }
        } catch (Exception e) {
            log.error("调用 Workflow 阻塞模式API异常", e);
            throw new RuntimeException("调用 Workflow API异常: " + e.getMessage(), e);
        }
    }
}
